import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { NulResponse, createRequest, createResponse } from "./common";
import { encryptSha256 } from "../util/Crypto";
import axios from "axios";
import { RootState } from "./store";

interface LoginRequest {
  userName: string;
  password: string;
};

interface CheckTokenResponse {
  valid: boolean;
};

interface LoginResponse {
  token: string;
  authority: string;
};

interface LoginState {
  loaded: boolean;
  loggedIn: boolean;
  token: string;
  authority: string;
};

const initialState: LoginState = {
  loaded: false,
  loggedIn: false,
  token: '',
  authority: ''
};

/**
 * Check if the token stored in the local storage is valid.
 * If the token is certificated, it will be stored in Redux.
 * @returns an object to dispatch
 */
export const checkToken = createAsyncThunk('api/checkToken', async (request: undefined, thunkAPI) => {
  const token: string | null = localStorage.getItem('token');
  if (token === null) {
    return thunkAPI.fulfillWithValue(createResponse({ valid: false }));
  }
  const response = await axios.post<NulResponse<CheckTokenResponse>>('http://localhost:8080/login/check', createRequest({
    token
  }));
  return response.data;
});

export const login = createAsyncThunk('api/login', async (request: LoginRequest, thunkAPI) => {
  const passwordHash: string = encryptSha256(request.password);
  const response = await axios.post<NulResponse<LoginResponse>>('http://localhost:8080/login/loginpage', createRequest({
    account: request.userName,
    password: passwordHash
  }));
  return response.data;
});

const authSlice = createSlice({
  name: 'login',
  initialState,
  reducers: {
    cleanAuth: (state) => {
      localStorage.removeItem('token');
      state.loggedIn = false;
      state.token = '';
    }
  },
  extraReducers: (builder) => {
    builder.addCase(login.fulfilled, (state, action) => {
      if (action.payload.code === 0) {
        const token: string = action.payload.result.token;
        const authority: string = action.payload.result.authority;
        state.loaded = true;
        state.loggedIn = true;
        state.token = token;
        state.authority = authority;
        localStorage.setItem('token', token);
        localStorage.setItem('authority', authority);
      }
    }).addCase(login.rejected, (state, action) => {
      console.error(action.error);
    }).addCase(checkToken.fulfilled, (state, action) => {
      if (action.payload.result.valid) {
        state.loggedIn = true;
        const token: string | null = localStorage.getItem('token');
        const authority: string | null = localStorage.getItem('authority');
        if (token !== null && authority !== null) {
          state.token = token;
          state.authority = authority;
        }
      }
      state.loaded = true;
    }).addCase(checkToken.rejected, (state, action) => {
      console.error(action.error);
    });
  }
});

export const { actions } = authSlice;
export const selectToken = (state: RootState) => state.auth.token;
export const selectTokenLoaded = (state: RootState) => state.auth.loaded;
export const selectLoggedIn = (state: RootState) => state.auth.loggedIn;
export const selectAuthority = (state: RootState) => state.auth.authority;
export default authSlice.reducer;